package top.broncho.mvvm.ui.widget

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.app.Activity
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.AnimationUtils
import android.view.animation.LinearInterpolator
import android.widget.ProgressBar
import androidx.core.view.ViewCompat
import top.broncho.gank.R

class CoolIndicator @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ProgressBar(context, attrs, defStyleAttr) {
    private lateinit var mPrimaryAnimator: ValueAnimator
    private val mShrinkAnimator = ValueAnimator.ofFloat(0f, 1f)
    private val mAlphaAnimator = ValueAnimator.ofFloat(1f, 0.25f)
    private var mClipRegion = 0f
    private var mExpectedProgress = 0
    private var mTempRect: Rect = Rect()
    private var mIsRtl = false
    private var mIsRunning = false
    private var mIsRunningCompleteAnimation = false
    private val mAccelerateDecelerateInterpolator = AccelerateDecelerateInterpolator()
    private val mLinearInterpolator = LinearInterpolator()
    private var mWrap = false
    private var mDuration = 0
    private var mResID = 0
    private val mClosingAnimatorSet: AnimatorSet = AnimatorSet()
    private val mListener =
        AnimatorUpdateListener { setProgressImmediately(mPrimaryAnimator.animatedValue as Int) }

    init {
        val a = context.obtainStyledAttributes(attrs, R.styleable.CoolIndicator)
        mDuration = a.getInteger(R.styleable.CoolIndicator_shiftDuration, 1000)
        mResID = a.getResourceId(R.styleable.CoolIndicator_shiftInterpolator, 0)
        mWrap = a.getBoolean(R.styleable.CoolIndicator_wrapShiftDrawable, true)
        mPrimaryAnimator = ValueAnimator.ofInt(progress, max)
        mPrimaryAnimator.interpolator = LinearInterpolator()
        mPrimaryAnimator.duration = (PROGRESS_DURATION * 0.92).toLong()
        mPrimaryAnimator.addUpdateListener(mListener)
        mPrimaryAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                if (progress == max) {
                    Log.i(
                        TAG,
                        "progress:$progress  max:$max"
                    )
                    animateClosing()
                }
            }
        })
        mAlphaAnimator.duration = CLOSING_DURATION.toLong()
        mAlphaAnimator.addUpdateListener { animation -> alpha = (animation.animatedValue as Float) }
        mAlphaAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationCancel(animation: Animator) {
                super.onAnimationCancel(animation)
                alpha = 1f
            }

            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                alpha = 1f
            }
        })
        mShrinkAnimator.duration = CLOSING_DURATION.toLong()
        mShrinkAnimator.interpolator = AccelerateDecelerateInterpolator()
        mShrinkAnimator.addUpdateListener { valueAnimator ->
            val region = valueAnimator.animatedValue as Float
            if (mClipRegion != region) {
                mClipRegion = region
                invalidate()
            }
        }
        mShrinkAnimator.addListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(animator: Animator) {
                mClipRegion = 0f
            }

            override fun onAnimationEnd(animator: Animator) {
                setVisibilityImmediately(View.GONE)
                mIsRunning = false
                mIsRunningCompleteAnimation = false
            }

            override fun onAnimationCancel(animator: Animator) {
                mClipRegion = 0f
                setVisibilityImmediately(View.GONE)
                mIsRunning = false
                mIsRunningCompleteAnimation = false
            }

            override fun onAnimationRepeat(animator: Animator) {}
        })
        mClosingAnimatorSet.playTogether(mShrinkAnimator, mAlphaAnimator)
        if (progressDrawable != null) {
            setProgressDrawableImmediately(
                buildWrapDrawable(
                    progressDrawable,
                    mWrap,
                    mDuration,
                    mResID
                )
            )
        }
        a.recycle()
        max = 100
    }

    private fun setProgressInternal(nextProgress: Int) {
        var nextProgress = nextProgress
        nextProgress = nextProgress.coerceAtMost(max)
        nextProgress = 0.coerceAtLeast(nextProgress)
        mExpectedProgress = nextProgress
        // a dirty-hack for reloading page.
        if (mExpectedProgress < progress && progress == max) {
            setProgressImmediately(0)
        }
        if (nextProgress == max) {
            Log.i(
                TAG,
                "finished duration:" + FINISHED_DURATION * (1 - java.lang.Float.valueOf(
                    progress.toFloat()
                ) / max)
            )
            mPrimaryAnimator.duration = (FINISHED_DURATION * (1 - java.lang.Float.valueOf(
                progress.toFloat()
            ) / max)).toLong()
            mPrimaryAnimator.interpolator = mAccelerateDecelerateInterpolator
        } else {
            mPrimaryAnimator.duration = (PROGRESS_DURATION * (1 - java.lang.Float.valueOf(
                progress.toFloat()
            ) / (max * 0.92))).toLong()
            mPrimaryAnimator.interpolator = mLinearInterpolator
        }
        mPrimaryAnimator.cancel()
        mPrimaryAnimator.setIntValues(progress, nextProgress)
        mPrimaryAnimator.start()
        if (nextProgress != max) { // stop closing animation
            mShrinkAnimator.cancel()
            mClipRegion = 0f
        }
    }

    private fun setProgressDrawableImmediately(drawable: Drawable) {
        super.setProgressDrawable(drawable)
    }

    override fun setProgressDrawable(d: Drawable) {
        super.setProgressDrawable(buildWrapDrawable(d, mWrap, mDuration, mResID))
    }

    fun start() {
        if (mIsRunning) {
            return
        }
        mIsRunning = true
        this.visibility = View.VISIBLE
        setProgressImmediately(0)
        setProgressInternal((max * LINEAR_MAX_RADIX_SEGMENT).toInt())
    }

    fun complete() {
        if (mIsRunningCompleteAnimation) {
            return
        }
        if (mIsRunning) {
            mIsRunningCompleteAnimation = true
            setProgressInternal((max * ACCELERATE_DECELERATE_MAX_RADIX_SEGMENT).toInt())
        }
    }

    @Synchronized
    override fun setMax(max: Int) {
        super.setMax(max * RADIX)
    }

    public override fun onDraw(canvas: Canvas) {
        if (mClipRegion == 0f) {
            super.onDraw(canvas)
        } else {
            canvas.getClipBounds(mTempRect)
            val clipWidth = mTempRect.width() * mClipRegion
            val saveCount = canvas.save()
            if (mIsRtl) {
                canvas.clipRect(
                    mTempRect.left.toFloat(),
                    mTempRect.top.toFloat(),
                    mTempRect.right - clipWidth,
                    mTempRect.bottom.toFloat()
                )
            } else {
                canvas.clipRect(
                    mTempRect.left + clipWidth,
                    mTempRect.top.toFloat(),
                    mTempRect.right.toFloat(),
                    mTempRect.bottom.toFloat()
                )
            }
            super.onDraw(canvas)
            canvas.restoreToCount(saveCount)
        }
    }

    override fun setVisibility(value: Int) {
        if (value == View.GONE) {
            if (mExpectedProgress == max) {
                //animateClosing();
            } else {
                setVisibilityImmediately(value)
            }
        } else {
            setVisibilityImmediately(value)
        }
    }

    private fun setVisibilityImmediately(value: Int) {
        super.setVisibility(value)
    }

    private fun animateClosing() {
        mIsRtl = ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL
        mClosingAnimatorSet.cancel()
        handler?.postDelayed({ mClosingAnimatorSet.start() }, CLOSING_DELAY.toLong())
    }

    private fun setProgressImmediately(progress: Int) {
        super.setProgress(progress)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mPrimaryAnimator.cancel()
        mClosingAnimatorSet.cancel()
        mShrinkAnimator?.cancel()
        mAlphaAnimator?.cancel()
    }

    private fun buildWrapDrawable(
        original: Drawable,
        isWrap: Boolean,
        duration: Int,
        resID: Int
    ): Drawable {
        return if (isWrap) {
            val interpolator =
                if (resID > 0) AnimationUtils.loadInterpolator(
                    context,
                    resID
                ) else null
            ShiftDrawable(original, duration, interpolator)
        } else {
            original
        }
    }

    companion object {
        private const val PROGRESS_DURATION = 6000
        private const val CLOSING_DELAY = 200
        private const val CLOSING_DURATION = 600
        private const val FINISHED_DURATION = 300
        private val TAG = CoolIndicator::class.java.simpleName
        private const val LINEAR_MAX_RADIX_SEGMENT = 0.92f
        private const val ACCELERATE_DECELERATE_MAX_RADIX_SEGMENT = 1f
        /**
         * 进度放大倍数
         */
        private const val RADIX = 100

        @JvmStatic
        fun create(activity: Activity): CoolIndicator {
            return CoolIndicator(
                activity,
                null,
                android.R.style.Widget_Material_ProgressBar_Horizontal
            )
        }
    }
}